import { KeepAlive, defineComponent, h, markRaw } from "vue";
import { defineStore } from "pinia";
import type { RouteRecordRaw } from "vue-router";
import cryptoRandomString from "crypto-random-string";

import customRoutes from "@/config/routes";
import router from "@/router";
import type { CustomRouteRaw, CustomRouteNormallized, CustomRouteMeta } from "@/router/type";
import { isLink } from "@/utils/regexp";

import { useUserStore } from "./user";
import { useTagsStore } from "./tags";
import { useSettingsStore } from "./settings";

const checkPathStart = /^\//;
const views = import.meta.glob("@/views/**/*.vue");
console.log("store[menu] views.", views);

type State = {
    inited: boolean;
    menus: CustomRouteNormallized[];
    routeNames: string[];
};

const DefaultRouteMeta = {
    affix: false,
    keepAlive: true,
    hideInMenu: false,
    hideInBreadcrumb: false,
    hideInTag: false,
    link: false,
    auth: true,
} as CustomRouteMeta;

const getIconName = (name: string) => {
    if (/^(el-icon-|ElIcon)/.test(name) || isLink(name) || /^(\.)*\//.test(name)) return name;
    if (/^[A-Z]/.test(name)) return `ElIcon${name}`;
    if (/^[a-z]/.test(name)) return `el-icon-${name}`;
    return name;
};

const filterdRoutes = (routes: CustomRouteRaw[], path = "") => {
    const arr = routes.map((item) => {
        item.meta = { ...DefaultRouteMeta, ...item.meta };

        if (!item.name) item.name = cryptoRandomString({ length: 10 });
        if (item.meta.icon) item.meta.icon = getIconName(item.meta.icon);

        if (!item.path) {
            if (item.link) {
                item.path = cryptoRandomString({ length: 10 });
                item.meta.link = true;
            } else {
                item.path = path;
            }
        }
        if (!checkPathStart.test(item.path)) {
            item.path = `${path}/${item.path}`;
        }
        if (item.redirect && !checkPathStart.test(item.redirect)) {
            item.redirect = `${item.path}/${item.redirect}`;
        }

        if (item.children && item.children.length) {
            item.children = filterdRoutes(item.children, item.path);
        }
        return item as CustomRouteNormallized;
    });
    return arr;
};

const checkPermissions = (menus: CustomRouteNormallized[], urls: string[], parent?: CustomRouteNormallized) => {
    const arr: CustomRouteNormallized[] = [];

    menus.forEach((menu) => {
        if (!menu.meta.auth || menu.path === "/") {
            arr.push(menu);
            return;
        }

        if (menu.children && menu.children.length) {
            const children = checkPermissions(menu.children, urls, menu);
            if (children.length) {
                menu.children = children;
                arr.push(menu);
            }
        } else {
            let path = menu.path;
            if (menu.meta.link) path = `${parent ? parent.path : ""}/${menu.link}`;
            if (urls.includes(path)) {
                arr.push(menu);
            }
        }
    });

    return arr;
};

const generateRoutes = (menus: CustomRouteRaw[], breadcrumb: any[] = [], systemKeepAlive = false) => {
    const arr: RouteRecordRaw[] = [];

    menus.forEach(({ component, children, ...route }: any) => {
        if (component) {
            const key = component.replace(/^@/, "/src");
            let _component: any = null;

            if (/\.vue$/.test(key)) {
                _component = views[key];
            } else {
                _component = views[`${key}/index.vue`] || views[`${key}.vue`];
            }
            route.component = createComponent(route.name, _component, systemKeepAlive && route.meta.keepAlive);
        }

        route.meta.breadcrumb = [...breadcrumb, route];
        arr.push(route);

        if (children && children.length) {
            arr.push(...generateRoutes(children, route.meta.breadcrumb));
        }
    });

    return arr;
};

const createComponent = (name: string, component: any, keepAlive = false) => {
    return defineComponent({
        name,
        data() {
            return { component: null, keepAlive };
        },
        async created() {
            try {
                const module = await component();
                this.component = markRaw(module.default);
            } catch (error) {
                console.error("load async component error.", error);
            }
        },
        render() {
            if (!this.component) return null;

            if (this.keepAlive) {
                return h(KeepAlive, {}, h(this.component));
            } else {
                return h(this.component);
            }
        },
    });
};

export const useMenuStore = defineStore("menu", {
    state: (): State => ({
        inited: false,
        menus: [],
        routeNames: [],
    }),
    actions: {
        async init() {
            const { token, urls } = useUserStore();
            if (this.inited || !token || !urls.length) return;
            const tags = useTagsStore();
            const settings = useSettingsStore();
            const routeNames: string[] = [];

            let menus = filterdRoutes(customRoutes);
            if (settings.auth) {
                menus = checkPermissions(menus, urls);
            }
            console.log("store[menu] init menus", menus);
            const routes = generateRoutes(menus, [], settings.keepAlive);
            console.log("store[menu] init routes", routes);
            this.$patch({ inited: true, menus, routeNames });

            routes.forEach((route) => {
                router.addRoute("layout", route);
                routeNames.push(route.name as string);
                if (route.meta!.affix) {
                    tags.addViewTag(route as any);
                }
            });
        },
        reset() {
            this.routeNames.forEach((name) => router.removeRoute(name));
            this.$reset();
        },
    },
});
